/**
 * 
 */
package gfx;

import game.Game;

import io.ImportGame;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

import javax.swing.*;

/**
 * @author Jean-Baptiste BORIC
 *
 */
public class FrameBuffer extends JPanel {
	
	private static final long serialVersionUID = 1L;
	
	private BufferedImage buffer[];
	private Graphics2D g2d[];

	private int frontBuffer;
	private int backBuffer;
	
	private int vWidth;
	private int vHeight;
	
	private ArrayList <Integer[]> pile;
	
	private Integer[] cur_transformation;
	
	public FrameBuffer(LayoutManager layout, int vWidth, int vHeight) {
		super(layout, true);
		
		frontBuffer = 0;
		backBuffer = 1;
		
		this.setVirtualSize(vWidth, vHeight);
		
		pile = new ArrayList<Integer[]>();
		this.cur_transformation = new Integer[2];
		this.cur_transformation[0] = 0;
		this.cur_transformation[1] = 0;
	}
	
	public FrameBuffer(int vWidth, int vHeight) {
		this(new FlowLayout(), vWidth, vHeight);
	}

	/**
	 * Set virtual screen size
	 * @param width New virtual width
	 * @param height New virtual height
	 * @return 
	 */
	public synchronized void setVirtualSize(int width, int height) {
		if(width < 1 || height < 1) {
			throw new IllegalArgumentException("Invalid virtual dimentions");
		}

		buffer = new BufferedImage[2];
		buffer[0] = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		buffer[1] = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		
		g2d = new Graphics2D[2];
		g2d[0] = buffer[0].createGraphics();
		g2d[1] = buffer[1].createGraphics();
		
		this.vWidth = width;
		this.vHeight = height;
	}
	
	
	
	/**
	 * @return the vWidth
	 */
	public int getVirtualWidth() {
		return vWidth;
	}

	/**
	 * @return the vHeight
	 */
	public int getVirtualHeight() {
		return vHeight;
	}

	/**
	 * Set virtual screen size
	 * @param width New virtual width
	 * @param height New virtual height
	 * @return 
	 */
	public synchronized void setVirtualSize(Dimension d) {
		setVirtualSize((int)d.getWidth(), (int)d.getHeight());
	}
	
	/**
	 * Rendering method (doublebuffered)
	 * @param g Graphic rendering context
	 */
	public synchronized void paint(Graphics g) {
		Graphics2D g2d = (Graphics2D)g;
		
		// Scale to real JPanel size while drawing 
		AffineTransform a = new AffineTransform();
		a.scale((double)(getWidth()) / vWidth, (double)(getHeight()) / vHeight);

		g2d.drawImage(buffer[frontBuffer], a, null);
		
		super.paintBorder(g);
		super.paintChildren(g);
	}
	
	/**
	 * Swap buffers for doublebuffering
	 */
	public synchronized void swapBuffers() {
		frontBuffer = (frontBuffer + 1) % 2;
		backBuffer = 1 - frontBuffer;
		
		// Clear back buffer
		
		g2d[backBuffer].setColor(Color.BLACK);
		g2d[backBuffer].fillRect(0, 0, vWidth, vHeight);
		
		repaint(0, 0, 0, getWidth(), getHeight());
	}
	
	/**
	 * Fill a rectangle on the FrameBuffer
	 * @param x X coordinate
	 * @param y Y coordinate
	 * @param w Width
	 * @param h Height
	 * @param c Color
	 */
	public synchronized void fillRect(int x, int y, int w, int h, Color c) {
		g2d[backBuffer].setColor(c);
		g2d[backBuffer].fillRect(x+this.cur_transformation[0], y+this.cur_transformation[1], w, h);
	}
	
	public synchronized void drawImage(GraphicImage image, int x, int y) {
		g2d[backBuffer].drawImage(image.getImage(), x, y, null);
	}
	
	/**
	 * Draw a sprite to the framebuffer
	 * @param sprite Sprite
	 */
	public synchronized void drawSprite(GraphicSprite sprite) {
		
		// on recup�re ici les dimensions de l'image
		int wi = sprite.getImage().getWidth ();
		int hi = sprite.getImage().getHeight ();
		
		// on recup�re les dimensions d'un sprite
		int ws = wi / sprite.getImage().getNbRows();
		int hs = hi / sprite.getImage().getNbColumns();
		
		//on cherche desormais la position du sprite � afficher, � partir de la frame
		int posRow = sprite.getFrame () % sprite.getImage().getNbRows();
		int posCol = sprite.getFrame() / sprite.getImage().getNbRows();
		
		//on cherche la position en pixels
		int xs = posRow*ws;
		int ys = posCol*hs;
		
		//[.. EN COURS DE TEST ..]
		BufferedImage bimage = new BufferedImage(ws,hs, BufferedImage.TYPE_INT_ARGB);
		Graphics2D gbimage = (Graphics2D) bimage.getGraphics();
		gbimage.drawImage(sprite.getImage().getImage(), -xs, -ys, null);

		g2d[backBuffer].drawImage(bimage, sprite.getX()+this.cur_transformation[0]
			 						 , sprite.getY()+this.cur_transformation[1], null);

		
	}
	
	/**
	 * Draw a string to the framebuffer
	 * @param string
	 */
	public synchronized void drawString(GraphicString string) {
		
		// we take the  image's weight
		int wi = string.getImage().getImage().getWidth (null);
		
		//then we take the sprite's weight
		int ws = wi / string.getImage().getNbRows();
		
		for (int i=0; i < string.getString().length(); i++){
			GraphicSprite gs = new GraphicSprite (string.getX() + ws*i, string.getY(), string.getString().charAt(i),string.getImage() );
			this.drawSprite(gs);
		}
	}
	
	/**
	 * Draw a tilemap to the framebuffer
	 * @param tilemap
	 */
	public synchronized void drawTileMap(GraphicTileMap tilemap) {
		// TODO: implement drawTileMap()
		
		int width = tilemap.getGraphicImage().getImage().getWidth(null);
		int height = tilemap.getGraphicImage().getImage().getHeight(null);
				
		int row = width / tilemap.getGraphicImage().getNbRows();
		int column = height / tilemap.getGraphicImage().getNbColumns();
		
		for (int i = 0 ; i < tilemap.getWidth() ; i++)
		{
			for (int j = 0 ; j < tilemap.getHeight() ; j++)
			{
				GraphicSprite temp = new GraphicSprite(tilemap.getX() + row*i,
													   tilemap.getY() + column*j,
													   tilemap.getTile(i, j),
													   tilemap.getGraphicImage());
				
				this.drawSprite(temp);
			}
		}
	}
	
	/**
	 * Push a new relative coordinate system on the transformation stack
	 * @param dx New relative X offset
	 * @param dy New relative Y offset
	 */
	public void pushTransformation(int dx, int dy) {
		
		Integer[] temp = new Integer[2];
		temp[0] = dx;
		temp[1] = dy;
		
		this.pile.add(temp);
		
		this.cur_transformation[0] = 0;
		this.cur_transformation[1] = 0;
		
		for(Integer[] i : this.pile)
		{
			this.cur_transformation[0] += i[0];
			this.cur_transformation[1] += i[1];
		}
		
	}
	
	/**
	 * Pop an item from the transformation stack
	 */
	public void popTransformation() {
		
		this.pile.remove(this.pile.size()-1);
		
		this.cur_transformation[0] = 0;
		this.cur_transformation[1] = 0;
		
		for(Integer[] i : this.pile)
		{
			this.cur_transformation[0] += i[0];
			this.cur_transformation[1] += i[1];
		}
	}
	
	/**
	 * Test for Framebuffer
	 * @param args Ignored
	 */
	
//	public static void main (String [] args) {
//		// on initialise un graph_image
//		GraphicImage graph_image = new GraphicImage ("test.png",0,0, 32,8);
//		
//		//on ouvre un jframe
//		JFrame frame = new JFrame ("ooo");
//		frame.setLayout(null);
//		frame.setBounds(500,100,700,700);
//		
//		//on cr�� un conteneur
//		Container cont = frame.getContentPane();
//		cont.setBackground (Color.green);
//		
//		//On cr�e un JLabel pour test l'image du GraphicImage
//		JLabel label = new JLabel ( );
//		label.setIcon(new ImageIcon ("test.png") );
//		label.setBounds(0,150,100,100);
//		cont.add(label);
//		
//		//on cr�� un bouton (pour tester l'ajout)
//	/*	JButton button = new JButton ("test");
//		button.setBounds(0,0,100,100);
//		cont.add(button);*/
//		
//		frame.setVisible(true);
//		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//	}
	
	
	
	// Main by JB
	
	public static void main(String[] args) {
		JFrame f = new JFrame("FrameBuffer test");
		FrameBuffer buffer = new FrameBuffer(320, 240);
		
		f.add(buffer);
		buffer.setBounds(0, 0, 640, 480);
		f.setSize(640, 480);
		f.setVisible(true);
		
		GraphicImage image = new GraphicImage("data/gfx/gui/gui_font_9x16.png", 0, 0, 32, 8);
		GraphicImage tile = new GraphicImage("data/gfx/units/ground/infanteryRED.png", 0, 0, 3, 2);
		GraphicSprite sprite = new GraphicSprite ( 50, 50, 63, image);
		GraphicString string = new GraphicString (0,200,"Hop hop hop", image);
	
		buffer.fillRect(0, 0, 160, 64, Color.BLUE);
		
		//buffer.swapBuffers();	
		
		GraphicTileMap p = new GraphicTileMap(tile, 8, 8);
		
		int x = 0;
		//while(true) {

			buffer.drawTileMap(p);
			buffer.drawSprite (sprite);
			buffer.drawString(string);
			buffer.pushTransformation(x%160, 0);
			buffer.pushTransformation(4, (x%80)-(x%40));
			
			
			
			buffer.popTransformation();
			buffer.popTransformation();
			
			buffer.swapBuffers();
			
			x++;
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		//}
		try
		{
			Game pp = new Game(null, null, null);
			ImportGame game = new ImportGame(new BufferedReader(new InputStreamReader(new FileInputStream("data/levels/level0.tsv"))));
			try 
			{
				game.read(pp);
				
				
				pp.getMap().resize(30, 300);
				System.out.println(pp.getMap());
			}
			catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} 
		catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
}
